package com.xht.authorization.server.oauth2.server.authorization;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.util.Assert;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <h1>描述 ：用于管理新的和现有的授权信息</h1>
 * <h2>它是存储新授权和查询现有授权的中心组件。</h2>
 * <p>其他组件在遵循特定的协议流时使用它 </p>
 * <li>例如，
 * <ul>
 *         <li>客户端身份验证</li>
 *         <li>授权授予处理</li>
 *         <li>令牌自省</li>
 *         <li>令牌撤销</li>
 *         <li>动态客户端注册</li>
 *         <li>.....等</li>
 * </ul>
 * </li>
 *
 * @author : 小糊涂
 * @version : 1.0
 **/
@Slf4j
@RequiredArgsConstructor
public class XhtOAuth2AuthorizationService implements OAuth2AuthorizationService {

    private final Map<String, OAuth2Authorization> authorizations = new ConcurrentHashMap<>();
    /**
     * 保存授权信息 {@link OAuth2Authorization}.
     *
     * @param authorization the {@link OAuth2Authorization}
     */
    @Override
    public void save(OAuth2Authorization authorization) {
        log.info("保存授权信息 {}", authorization.getId());
        this.authorizations.put(authorization.getId(), authorization);
    }

    /**
     * 删除授权信息 {@link OAuth2Authorization}.
     *
     * @param authorization the {@link OAuth2Authorization}
     */
    @Override
    public void remove(OAuth2Authorization authorization) {
        log.info("删除授权信息 {}",authorization.getId());
        this.authorizations.remove(authorization.getId());
    }

    /**
     * 返回由提供的{@code id}标识的{@link OAuth2Authorization};
     * 如果没有找到，则返回{@code null}。
     *
     * @param id 授权标识符
     * @return 如果找到，则返回{@link OAuth2Authorization}，否则返回{@code null}
     */
    @Override
    public OAuth2Authorization findById(String id) {
        log.info("根据id={} 查询授权信息", id);
        return this.authorizations.get(id);
    }

    /**
     * 返回包含提供的令牌的{@link OAuth2Authorization}，
     * 如果没有找到，则返回{@code null}。
     *
     * @param token     令牌凭证
     * @param tokenType 令牌类型 {@link OAuth2TokenType token type}
     * @return 如果找到，则返回{@link OAuth2Authorization}，否则返回{@code null}
     */
    @Override
    public OAuth2Authorization findByToken(String token, OAuth2TokenType tokenType) {
        log.info("根据token={}, tokenType={} 查询授权信息", token, tokenType);
        Assert.hasText(token, "token cannot be empty");
        for (OAuth2Authorization authorization : this.authorizations.values()) {
            log.info(authorization.getId());
            if (hasToken(authorization, token, tokenType)) {
                return authorization;
            }
        }
        return null;
    }
    private static boolean hasToken(OAuth2Authorization authorization, String token, @Nullable OAuth2TokenType tokenType) {
        if (tokenType == null) {
            return matchesState(authorization, token) ||
                    matchesAuthorizationCode(authorization, token) ||
                    matchesAccessToken(authorization, token) ||
                    matchesRefreshToken(authorization, token);
        } else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {
            return matchesState(authorization, token);
        } else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) {
            return matchesAuthorizationCode(authorization, token);
        } else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) {
            return matchesAccessToken(authorization, token);
        } else if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) {
            return matchesRefreshToken(authorization, token);
        }
        return false;
    }
    private static boolean matchesState(OAuth2Authorization authorization, String token) {
        return token.equals(authorization.getAttribute(OAuth2ParameterNames.STATE));
    }
    private static boolean matchesAuthorizationCode(OAuth2Authorization authorization, String token) {
        OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode =
                authorization.getToken(OAuth2AuthorizationCode.class);
        return authorizationCode != null && authorizationCode.getToken().getTokenValue().equals(token);
    }

    private static boolean matchesAccessToken(OAuth2Authorization authorization, String token) {
        OAuth2Authorization.Token<OAuth2AccessToken> accessToken =
                authorization.getToken(OAuth2AccessToken.class);
        return accessToken != null && accessToken.getToken().getTokenValue().equals(token);
    }

    private static boolean matchesRefreshToken(OAuth2Authorization authorization, String token) {
        OAuth2Authorization.Token<OAuth2RefreshToken> refreshToken =
                authorization.getToken(OAuth2RefreshToken.class);
        return refreshToken != null && refreshToken.getToken().getTokenValue().equals(token);
    }
}
